package tools

import java.nio.file.Files

import config.{Global, MyFile}
import org.apache.commons.io.FileUtils
import play.api.data.Form
import play.api.data.FormBinding.Implicits._
import play.api.data.Forms._
import play.api.libs.json.{JsObject, Json}
import play.api.mvc.{AnyContent, Request}
import utils.ExecCommand

import scala.collection.mutable
import scala.jdk.CollectionConverters._

object Enrich extends MyFile {

  case class EnrichData(method: String, id: String, species: String, pval: String)

  val EnrichForm: Form[EnrichData] = Form(
    mapping(
      "method" -> text,
      "id" -> text,
      "species" -> text,
      "pval" -> text
    )(EnrichData.apply)(EnrichData.unapply)
  )

  def enrichRun(implicit request: Request[AnyContent]): JsObject = {
    val data = EnrichForm.bindFromRequest.get
    val geneId = data.id.split(",").map(_.trim).distinct.toBuffer

    val tmpDir = Files.createTempDirectory("tmpDir").toString
    FileUtils.writeLines(s"$tmpDir/study.txt".toFile, geneId.asJava)

    val json = data.method match {
      case "kegg" => keggResult(data, tmpDir, geneId)
      case "go" => goResult(data, tmpDir)
    }
    tmpDir.delete
    json
  }

  def keggResult(data: EnrichData, tmpDir: String, geneId: mutable.Buffer[String]) = {
    val path = s"${Global.path}/enrichData"
    val out = s"$tmpDir/KEGG_enrichment.txt"
    val execCommand = new ExecCommand
    val pop = s"$path/${data.species}.gene".readLines.intersect(geneId)
    FileUtils.copyFile(s"$path/${data.species}.kegg".toFile, s"$tmpDir/bg.txt".toFile)
    val bg = s"$tmpDir/bg.txt".readLines.map(_.split("\t")).map(x => x.head -> x.last).toMap
    val fg = pop.map { x =>
      x + "\t" + bg.getOrElse(x, "None")
    }
    FileUtils.writeLines(s"$tmpDir/fg.txt".toFile, fg.asJava)

    val command = s"python ${Global.toolsPath}/kobas2.0-20150126/identify.py -f $tmpDir/fg.txt -b $tmpDir/bg.txt -m b -n BH -c 5 -o $out"
    FileUtils.writeStringToFile(s"$tmpDir/run.sh".toFile,command)

    val c = s"perl ${Global.toolsPath}/identify.pl -study=$tmpDir/study.txt -population=$path/${data.species}.gene" +
      s" -association=$path/${data.species}.kegg -m=b -n=BH -o=$out -c=5 -maxp=${data.pval}"
    println(command)
    execCommand.exect(Array(command), tmpDir)
    if (execCommand.isSuccess) {
      val json = out.readLines.tail.map(_.split("\t")).filter(_.length == 9).filter(_(5).toDouble <= data.pval.toDouble).map { all =>
        val hyper = "<a target='_blank' href='" + all(8) + "'><input type='button' class='link' value='linked'></a><a style='display: none'>" + all(8) + "</a>"
        Json.obj("term" -> all.head, "database" -> all(1), "id" -> all(2), "input_num" -> all(3), "back_num" -> all(4),
          "p-value" -> all(5), "correct_pval" -> all(6), "input" -> all(7), "hyperlink" -> hyper)
      }
      Json.obj("valid" -> "true", "data" -> json)
    } else {
      Json.obj("valid" -> "false", "msg" -> execCommand.getErrStr)
    }
  }

  def goResult(data: EnrichData, tmpDir: String): JsObject = {
    val path = s"${Global.path}/enrichData"
    val out = s"$tmpDir/GO_enrichment.txt"
    val execCommand = new ExecCommand
    val command = s"python ${Global.toolsPath}/goatools-0.5.7/scripts/find_enrichment.py --alpha=0.05 --pval=${data.pval} " +
      s" --output $out $tmpDir/study.txt $path/${data.species}.gene $path/${data.species}.go"
    execCommand.exect(Array(command), tmpDir)
    if (execCommand.isSuccess) {
      val json = out.readLines.tail.map { x =>
        val all = x.split("\t")
        val goLink = "<a target='_blank' href='http://amigo.geneontology.org/amigo/term/" + all(0) + "'>" + all(0) + "</a>"
        Json.obj("id" -> goLink, "enrichment" -> all(1), "description" -> all(2), "ratio_in_study" -> all(3),
          "ratio_in_pop" -> all(4), "p_uncorrected" -> all(5), "p_bonferroni" -> all(6), "p_holm" -> all(7),
          "p_sidak" -> all(8), "p_fdr" -> all(9), "namespace" -> all(10), "genes_in_study" -> all(11))
      }
      Json.obj("valid" -> "true", "data" -> json)
    } else {
      Json.obj("valid" -> "false", "msg" -> execCommand.getErrStr)
    }
  }

}
